﻿using Sidvall.WorkItemManager.Data;
using Sidvall.WorkItemManager.Net.Services.V1;
using System.Linq;

namespace Sidvall.WorkItemManager.Model.DataPortalManagers
{
    public partial class WorkItemManager<TEntity, TList>
    {
        #region Public Members

        #region DeleteItemByPrimaryKeyAsync

        public async System.Threading.Tasks.Task<int> DeleteItemByPrimaryKeyAsync(int serverConnectionId, long workItemId)
        {
            global::Sidvall.Data.DeleteCriteria<global::Sidvall.WorkItemManager.Data.Fields.WorkItemField> criteria;

            criteria = Sidvall.Data.CriteriaManager.NewDeleteCriteria<global::Sidvall.WorkItemManager.Data.Fields.WorkItemField>();
            criteria.Filter = new global::Sidvall.Data.Filter<global::Sidvall.WorkItemManager.Data.Fields.WorkItemField>();
            criteria.Filter.AddFilterItem(global::Sidvall.WorkItemManager.Data.Fields.WorkItemField.WorkItemId, Sidvall.Data.ComparisonOperator.Equal, workItemId);
            ParameterManager.InitializeParameters(criteria, serverConnectionId);

            return await DeleteItemsAsync(criteria).ConfigureAwait(false);
        }

        #endregion
        #region GetItemByPrimaryKeyAsync

        public async System.Threading.Tasks.Task<TEntity> GetItemByPrimaryKeyAsync(int serverConnectionId, long workItemId)
        {
            return await GetItemByPrimaryKeyAsync(serverConnectionId, workItemId, null).ConfigureAwait(false);
        }
        public async System.Threading.Tasks.Task<TEntity> GetItemByPrimaryKeyAsync(int serverConnectionId, long workItemId,
            System.Collections.Generic.IEnumerable<global::Sidvall.WorkItemManager.Data.Relations.WorkItemRelation> includedEntities)
        {
            var criteria = Sidvall.Data.CriteriaManager.NewFetchCriteria<global::Sidvall.WorkItemManager.Data.Fields.WorkItemField, global::Sidvall.WorkItemManager.Data.Relations.WorkItemRelation>(null, null, includedEntities, null, null);
            criteria.Filter = new global::Sidvall.Data.Filter<global::Sidvall.WorkItemManager.Data.Fields.WorkItemField>();
            criteria.Filter.AddFilterItem(global::Sidvall.WorkItemManager.Data.Fields.WorkItemField.WorkItemId, Sidvall.Data.ComparisonOperator.Equal, workItemId);
            ParameterManager.InitializeParameters(criteria, serverConnectionId);

            return await GetItemAsync(criteria).ConfigureAwait(false);
        }
        public async System.Threading.Tasks.Task<TEntity> GetItemByPrimaryKeyAsync(ServerConnection serverConnection, long workItemId)
        {
            return await GetItemByPrimaryKeyAsync(serverConnection, workItemId, null).ConfigureAwait(false);
        }
        public async System.Threading.Tasks.Task<TEntity> GetItemByPrimaryKeyAsync(ServerConnection serverConnection, long workItemId,
            System.Collections.Generic.IEnumerable<global::Sidvall.WorkItemManager.Data.Relations.WorkItemRelation> includedEntities)
        {
            var criteria = Sidvall.Data.CriteriaManager.NewFetchCriteria<global::Sidvall.WorkItemManager.Data.Fields.WorkItemField, global::Sidvall.WorkItemManager.Data.Relations.WorkItemRelation>(null, null, includedEntities, null, null);
            criteria.Filter = new global::Sidvall.Data.Filter<global::Sidvall.WorkItemManager.Data.Fields.WorkItemField>();
            criteria.Filter.AddFilterItem(global::Sidvall.WorkItemManager.Data.Fields.WorkItemField.WorkItemId, Sidvall.Data.ComparisonOperator.Equal, workItemId);
            ParameterManager.InitializeParameters(criteria, serverConnection);

            return await GetItemAsync(criteria).ConfigureAwait(false);
        }

        #endregion
        #region GetItemsByPrimaryKeysAsync

        public async System.Threading.Tasks.Task<TList> GetItemsByPrimaryKeysAsync(ServerConnection serverConnection, System.Collections.Generic.IEnumerable<long> keys, global::Sidvall.Data.FetchListCriteria<global::Sidvall.WorkItemManager.Data.Fields.WorkItemField, global::Sidvall.WorkItemManager.Data.Relations.WorkItemRelation> criteria, string strategyContext)
        {
            if ((keys == null) || (!keys.Any()))
                return (TList)this.DataPortalManager.ResourceFactory.CreateItems(global::Sidvall.WorkItemManager.Data.Model.DomainObjects.WorkItem);
            if (criteria == null)
                criteria = Sidvall.Data.CriteriaManager.NewFetchListCriteria<global::Sidvall.WorkItemManager.Data.Fields.WorkItemField, global::Sidvall.WorkItemManager.Data.Relations.WorkItemRelation>();
            if (criteria.Filter == null)
                criteria.Filter = new global::Sidvall.Data.Filter<global::Sidvall.WorkItemManager.Data.Fields.WorkItemField>();
            criteria.Filter.AddFilterItem(global::Sidvall.WorkItemManager.Data.Fields.WorkItemField.WorkItemId, Sidvall.Data.ComparisonOperator.Equal, keys);
            ParameterManager.InitializeParameters(criteria, serverConnection);
            return await GetItemsAsync(criteria, strategyContext, null).ConfigureAwait(false);
        }

        #endregion
        #region GetItemsAsync

        public async System.Threading.Tasks.Task<TList> GetItemsAsync(int? serverConnectionId, System.Collections.Generic.IEnumerable<global::Sidvall.WorkItemManager.Data.Relations.WorkItemRelation> includedEntities)
        {
            var criteria = Sidvall.Data.CriteriaManager.NewFetchListCriteria<global::Sidvall.WorkItemManager.Data.Fields.WorkItemField, global::Sidvall.WorkItemManager.Data.Relations.WorkItemRelation>(null, null, includedEntities, null, null);
            ParameterManager.InitializeParameters(criteria, serverConnectionId);
            return await GetItemsAsync(criteria).ConfigureAwait(false);
        }
        public async System.Threading.Tasks.Task<TList> GetItemsAsync(ServerConnection serverConnection, System.Collections.Generic.IEnumerable<global::Sidvall.WorkItemManager.Data.Relations.WorkItemRelation> includedEntities)
        {
            var criteria = Sidvall.Data.CriteriaManager.NewFetchListCriteria<global::Sidvall.WorkItemManager.Data.Fields.WorkItemField, global::Sidvall.WorkItemManager.Data.Relations.WorkItemRelation>(null, null, includedEntities, null, null);
            ParameterManager.InitializeParameters(criteria, serverConnection);
            return await GetItemsAsync(criteria).ConfigureAwait(false);
        }

        #endregion
        #region GetItemCountAsync

        public async System.Threading.Tasks.Task<long> GetItemCountAsync(int? serverConnectionId, long workItemTypeId)
        {
            global::Sidvall.Data.FetchCountCriteria<global::Sidvall.WorkItemManager.Data.Fields.WorkItemField> criteria;

            criteria = Sidvall.Data.CriteriaManager.NewFetchCountCriteria<global::Sidvall.WorkItemManager.Data.Fields.WorkItemField>();
            criteria.Filter = new Sidvall.Data.Filter<Data.Fields.WorkItemField>();
            criteria.Filter.AddFilterItem(Data.Fields.WorkItemField.WorkItemTypeId, Sidvall.Data.ComparisonOperator.Equal, workItemTypeId);
            ParameterManager.InitializeParameters(criteria, serverConnectionId);

            return await GetItemCountAsync(criteria).ConfigureAwait(false);
        }

        #endregion
        #region InitializeSensorsAsync

        public async System.Threading.Tasks.Task InitializeSensorsAsync(int serverConnectionId, long workItemId)
        {
            WorkItemSensor workItemSensor;

            // Load from database
            var workItemRelations = new System.Collections.Generic.List<Data.Relations.WorkItemRelation>();
            workItemRelations.Add(Data.Relations.WorkItemRelation.WorkItemSensors);
            var workItem = await Sidvall.WorkItemManager.SystemContext.Current.DataPortal.WorkItemManager.GetItemByPrimaryKeyAsync(serverConnectionId, workItemId, workItemRelations).ConfigureAwait(false);
            var workItemType = await Sidvall.WorkItemManager.SystemContext.Current.DataPortal.WorkItemTypeManager.GetItemByPrimaryKeyAsync(serverConnectionId, workItem.WorkItemTypeId.Value).ConfigureAwait(false);
            var workItemProvider = Sidvall.WorkItemManager.SystemContext.Current.EntityFacade.WorkItemTypeManager.GetWorkItemProvider(workItemType, workItem);
            var sensorIndex = new System.Collections.Generic.Dictionary<string, Sensor>(System.StringComparer.OrdinalIgnoreCase);
            var sensors = await workItemProvider.GetSensorsAsync().ConfigureAwait(false);
            if (sensors != null)
            {
                foreach (var sensor in sensors)
                {
                    if (string.IsNullOrWhiteSpace(sensor.Name))
                        continue;
                    if (!sensorIndex.ContainsKey(sensor.Name))
                        sensorIndex.Add(sensor.Name, sensor);
                }
            }

            // Remove unused sensors
            var iMax = workItem.WorkItemSensors.Items.Count;
            for (int i = iMax - 1; i >= 0; i--)
            {
                workItemSensor = workItem.WorkItemSensors.Items[i];
                if (!sensorIndex.ContainsKey(workItemSensor.Name))
                    workItem.WorkItemSensors.Remove(workItemSensor);
            }

            // Add sensors
            foreach (var keyValue in sensorIndex)
            {
                workItemSensor = (from o in workItem.WorkItemSensors.Items
                                  where o.Name.Equals(keyValue.Value.Name, System.StringComparison.OrdinalIgnoreCase)
                                  select o).FirstOrDefault();
                if (workItemSensor == null)
                {
                    workItemSensor = new WorkItemSensor()
                    {
                        WorkItemId = workItem.WorkItemId.Value,
                        Name = keyValue.Value.Name,
                        Description = keyValue.Value.Description,
                        Value = string.Empty,
                    };
                    workItem.WorkItemSensors.Add(workItemSensor);
                }
            }

            // Save changes
            workItem = await workItem.SaveItemAsync(Sidvall.Data.SaveMode.ForceUpdate).ConfigureAwait(false);
        }

        #endregion
        #region MoveItemAsync

        public async System.Threading.Tasks.Task MoveItemAsync(int sourceServerConnectionId, long sourceWorkerId, long sourceWorkItemId,
            int destinationServerConnectionId, long destinationWorkerId, long? destinationWorkItemId)
        {
            global::Sidvall.Data.FetchListCriteria<global::Sidvall.WorkItemManager.Data.Fields.WorkItemField, global::Sidvall.WorkItemManager.Data.Relations.WorkItemRelation> criteria;
            System.Collections.Generic.List<long> workerIds;
            TList items;
            IWorkItem sourceItem;
            long? workerId;
            int executionOrder, sourceExecutionOrder;

            if (sourceServerConnectionId == destinationServerConnectionId)
            {
                workerIds = new System.Collections.Generic.List<long>();
                workerIds.Add(sourceWorkerId);
                if (!workerIds.Contains(destinationWorkerId))
                    workerIds.Add(destinationWorkerId);

                criteria = Sidvall.Data.CriteriaManager.NewFetchListCriteria<global::Sidvall.WorkItemManager.Data.Fields.WorkItemField, global::Sidvall.WorkItemManager.Data.Relations.WorkItemRelation>();
                criteria.Filter = new global::Sidvall.Data.Filter<global::Sidvall.WorkItemManager.Data.Fields.WorkItemField>();
                criteria.Filter.AddFilterItem(global::Sidvall.WorkItemManager.Data.Fields.WorkItemField.WorkerId, Sidvall.Data.ComparisonOperator.Equal, workerIds);
                criteria.SortItems = new Sidvall.Data.SortItemCollection<Data.Fields.WorkItemField>();
                criteria.SortItems.Add(Data.Fields.WorkItemField.WorkerId, true);
                criteria.SortItems.Add(Data.Fields.WorkItemField.ExecutionContext, true);
                ParameterManager.InitializeParameters(criteria, sourceServerConnectionId);
                items = await GetItemsAsync(criteria).ConfigureAwait(false);
                sourceItem = (from o in items.DataItems
                              where o.WorkItemId == sourceWorkItemId
                              select o).FirstOrDefault();
                sourceItem.WorkerId = destinationWorkerId;
                var orderedItems = (from o in items.DataItems
                                    orderby o.WorkerId, o.ExecutionOrder
                                    select o);
                executionOrder = 0;
                workerId = null;
                sourceExecutionOrder = 1;
                foreach (var orderedItem in orderedItems)
                {
                    if (orderedItem.WorkItemId == sourceWorkItemId)
                        continue;
                    if ((workerId == null) || (workerId != orderedItem.WorkerId))
                    {
                        workerId = orderedItem.WorkerId;
                        executionOrder = 0;
                    }
                    executionOrder += 1;
                    orderedItem.ExecutionOrder = executionOrder;
                    if (orderedItem.WorkerId == destinationWorkerId)
                    {
                        if (destinationWorkItemId == null)
                        {
                            if (executionOrder == 1)
                            {
                                executionOrder = 2;
                                orderedItem.ExecutionOrder = executionOrder;
                            }
                        }
                        else if (orderedItem.WorkItemId == destinationWorkItemId)
                        {
                            executionOrder += 1;
                            sourceExecutionOrder = executionOrder;
                        }
                    }
                }
                sourceItem.ExecutionOrder = sourceExecutionOrder;
                await SaveItemsAsync(items, Sidvall.Data.SaveMode.ForceUpdate).ConfigureAwait(false);
            }
        }

        #endregion

        #endregion
    }
}